package com.tool.payment.service.impl;

import java.io.File;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.tool.payment.PaymentConfig;
import com.tool.payment.billparser.WeixinBillParser;
import com.tool.payment.enums.ModeEnum;
import com.tool.payment.exception.SDKRuntimeException;
import com.tool.payment.model.PayOrder;
import com.tool.payment.model.PayParams;
import com.tool.payment.service.AbstractPaymentService;
import com.tool.payment.service.PaymentService;
import com.tool.payment.util.DateUtil;
import com.tool.payment.util.HttpRequest;
import com.tool.payment.util.HttpsRequest;
import com.tool.payment.util.IdWorkerUtil;
import com.tool.payment.util.XMLParser;

/**
 * 微信支付(公众号支付，扫码支付)
 * @author huff
 *
 */
@Service
public class WeixinPaymentServiceImpl extends AbstractPaymentService implements PaymentService {
	
	private static final Logger logger = org.slf4j.LoggerFactory.getLogger(WeixinPaymentServiceImpl.class);
	
	/****************************支付*****************************************/
	@Override
	public Map<String, String> createOrder(String product,String mode,String notifyUrl,HttpServletRequest request) {
		PayParams params = new PayParams(product, 
							mode, 
							Integer.valueOf(request.getParameter("money")), 
							request.getParameter("workid"), 
							Long.valueOf(request.getParameter("userid")), 
							request.getParameter("openid"), 
							this.getUrl(request));
		return this.createOrder(notifyUrl, params, request);
	}
	
	@Override
	public Map<String, String> createOrder(String notifyUrl,PayParams params,HttpServletRequest request) {
				
		Map<String, String> resultMap = new HashMap<String, String>();
		try {
			long workid = Long.parseLong(String.valueOf(params.getWorkid()));//订单id
			int money = params.getMoney();
			long operateid = params.getUserid();
			String mode = params.getMode();
			String openid = params.getOpenid();
			PayOrder payOrder = new PayOrder(IdWorkerUtil.getId(), PayOrder.PAYPRODUCT_WEIXIN, PayOrder.TYPE_USERPAY, workid, money, openid, operateid);
			
			payOrderService.savePayOrder(payOrder);
			
			logger.info("开始创建微信预支付单:"+payOrder+"-"+request.getRemoteAddr()+"-"+mode+"-"+money);
			
			notifyUrl = params.getPreurl() + notifyUrl;
			
			//开始创建微信预支付订单
			Map<String, String> weixinPreorderMap = this.getWeiXinPreOrder(request,mode,notifyUrl, payOrder);
			if(weixinPreorderMap != null){
				resultMap.put("appId", weixinPreorderMap.get("appid").toString());
				resultMap.put("timeStamp", PaymentConfig.getInstance().getApiWeixinTimeStamp());
				resultMap.put("nonceStr", weixinPreorderMap.get("nonce_str"));
				resultMap.put("package", "prepay_id="+weixinPreorderMap.get("prepay_id"));
				resultMap.put("signType", PaymentConfig.getInstance().getSignType());
				String paySign = PaymentConfig.getInstance().getSignStr(resultMap);
//				resultMap.put("prepay_id", weixinPreorderMap.get("prepay_id"));
				resultMap.put("paySign", paySign);
			}
			
			if(mode.equalsIgnoreCase(ModeEnum.NATIVE.name)) {
				resultMap.put("code_url", weixinPreorderMap.get("code_url"));
				String url = params.getPreurl() + "pay/weixin/dopayqr?codeurl=" + weixinPreorderMap.get("code_url");
				StringBuffer sb = new StringBuffer();
				sb.append("<img id='tempContainer' src='' />");
				sb.append("<script>document.getElementById('tempContainer').src='"+url+"';</script>");
			 
				resultMap.put("html", sb.toString());
			}
			
		} catch (Exception e) {
			logger.error("处理微信 订单状态失败",e);
		}
		return resultMap;
	}
	/**
	 * 生成预处理订单
	 * @param clientIp
	 * @param tradeType
	 * @param money
	 * @return
	 */
	private Map<String, String> getWeiXinPreOrder(HttpServletRequest request,String mode,String notifyUrl,PayOrder payOrder) {
		
		PaymentConfig config = PaymentConfig.getInstance();
		
		Map<String, String> readySignMap = this.getWeiXinCommonParam(config);
		Map<String, String> resultMap = new HashMap<String, String>();
		
//		readySignMap.put("appid", config.getApiWeixinAppId());
//		readySignMap.put("mch_id", config.getApiWeixinMchId());//商户号
//		readySignMap.put("nonce_str", config.getApiWeixinNonceStr());
		readySignMap.put("device_info", "WEB");//设备号
		readySignMap.put("body", config.getApiWeixinBody());//商品描述
		readySignMap.put("out_trade_no", payOrder.getId().toString());//商户订单号
		
		readySignMap.put("total_fee", payOrder.getMoney().toString());//总金额，以分为单位
		readySignMap.put("spbill_create_ip", request.getRemoteAddr());//订单生成的机器 IP
		readySignMap.put("notify_url", notifyUrl);
		readySignMap.put("trade_type",mode.toUpperCase());
		if(mode.equalsIgnoreCase(ModeEnum.JSAPI.name)){
			readySignMap.put("openid", payOrder.getPayaccount());
		}
		try {
			readySignMap.put("sign", config.getSignStr(readySignMap));
		} catch (SDKRuntimeException e) {

			logger.error("生成微信预订单签名失败"+e.getMessage());
		}
		
		//签名结束开始调用微信post接口生成微信预订单
		String xmlInfo = XMLParser.getXmlStrFromMap(readySignMap);
		
		logger.info("requestxmlInfo："+xmlInfo);
		String postResult = HttpRequest.sendPost(config.getWeixinPreorderUrl(), xmlInfo);
		logger.info("postResutl: "+postResult);
		
		try {
			resultMap = XMLParser.parseXml(postResult);
		} catch (Exception e) {
			
			logger.info("获取微信预订单xmlstr 转换Map<String, String>失败: "+e.getMessage());
		}  
		
		return resultMap;
	}
	
	@Override
	@Transactional
	public String payNotify(HttpServletRequest request) {
		String result = "FAIL";
		int status = PayOrder.STATUS_FAIL;
		Map<String, String> requestParams = null;
		try {
			String xmlinfo = HttpRequest.requestToString(request);
			logger.info("支付回调通知:" + xmlinfo);
			requestParams = XMLParser.parseXml((xmlinfo));
		} catch (Exception e) {
			logger.error("处理 微信payNotify xml信息失败");
		}
		
		boolean flag = weixinVerify(requestParams);
		
		if(flag) {
			boolean resultflag = handleWeiXinNotify(requestParams);
			if(resultflag) {//支付成功
				result = "SUCCESS";
				status = PayOrder.STATUS_SUCCESS;
			} 
			Object payorderid = requestParams.get("out_trade_no");
			PayOrder payOrder = this.updatePayOrder(payorderid,status,requestParams.get("transaction_id").toString(), true);
			this.notifyService.weixinPayNotify(payOrder,status);
		}
		
		return result;
	}
	
	
	/**************************退款*****************************************/
	
	@Override
	public Map<String, String> refundOrder(PayParams params) {
		long workid = Long.valueOf(String.valueOf(params.getWorkid()));
		long userid = params.getUserid();
		return this.refundOrder(workid, userid);
	}
	
	private Map<String, String> refundOrder(long workid,long userid) {
		PayOrder zhifuOrder = this.payOrderService.getSuccessPayOrderByWorkid(workid, PayOrder.PAYPRODUCT_WEIXIN, PayOrder.TYPE_USERPAY);
		if(zhifuOrder == null) {
			throw new RuntimeException("微信退款参数->该微信支付订单不存在，或者订单尚未支付成功");
		}
		
		PayOrder refundOrder = new PayOrder(IdWorkerUtil.getId(), PayOrder.PAYPRODUCT_WEIXIN, PayOrder.TYPE_REFUNDTOUSER,
												workid, zhifuOrder.getMoney() * -1, "", userid);
		
		PaymentConfig config = PaymentConfig.getInstance();
		
		Map<String, String> paramMap = this.getWeiXinCommonParam(config);
//		paramMap.put("appid", config.getApiWeixinAppId());
//		paramMap.put("mch_id", config.getApiWeixinMchId());
//		paramMap.put("nonce_str",config.getApiWeixinNonceStr());
		if(StringUtils.isEmpty(zhifuOrder.getOuttradeno())) {
			paramMap.put("out_trade_no", String.valueOf(zhifuOrder.getId()));
		} else {
			paramMap.put("transaction_id", zhifuOrder.getOuttradeno());
		}
		paramMap.put("out_refund_no", String.valueOf(refundOrder.getId()));
		//所退款单总金额，微信金额单位-->分
		paramMap.put("total_fee", String.valueOf(zhifuOrder.getMoney()));
		//本次退款金额，默认全额退款
		paramMap.put("refund_fee", String.valueOf(zhifuOrder.getMoney()));
		paramMap.put("op_user_id", config.getApiWeixinMchId());
		try {
			paramMap.put("sign", config.getSignStr(paramMap));
		} catch (SDKRuntimeException e) {
			logger.error("生成微信预订单签名失败"+e.getMessage());
		}
		
		//签名结束开始调用微信post接口生成微信预订单 
		String xmlInfo = XMLParser.getXmlStrFromMap(paramMap);
		logger.info("微信退款请求参数："+xmlInfo);
		Map<String, String> refunded = new HashMap<String, String>();
		try {
			HttpsRequest request = new HttpsRequest(config.getApiWeixinCertLocalPath(), config.getApiWeixinCertPassword());

			String result = request.post(config.getWeixinRefundUrl(), xmlInfo, config.getApiWeixinCertLocalPath(), config.getApiWeixinCertPassword());
			logger.info("微信退款响应结果："+result);
			Map<String,String> resultMap = null;
			if(null != result && !"".equals(result)){
				resultMap = XMLParser.parseXml(result);
			}
			refunded.put("code", "fail");
			if("SUCCESS".equals(resultMap.get("result_code"))){
				refundOrder.setStatus(PayOrder.STATUS_SUCCESS);
				refunded.put("code", "success");
				refundOrder.setOuttradeno(resultMap.get("refund_id"));
			} else {
				refundOrder.setStatus(PayOrder.STATUS_FAIL);
				refunded.put("message", resultMap.get("err_code_des"));
			}
			refundOrder.setUpdatetime(new Date());
			this.notifyService.weixinRefundNotify(refundOrder);
			this.payOrderService.savePayOrder(refundOrder);
		} catch (Exception e) {
			logger.info("微信退款请求异常：",e);
			refunded.put("message", e.getMessage());
		}
		return refunded;
	}
	
	/**
	 * 退款状态查询
	 * @return
	 */
	public Map<String, String> refundQuery(Long payorderid) {
		PayOrder payOrder = this.payOrderService.getPayOrderById(payorderid);
		PaymentConfig config = PaymentConfig.getInstance();
		Map<String, String> paramMap = this.getWeiXinCommonParam(config);
		if(StringUtils.isEmpty(payOrder.getOuttradeno())) {
			paramMap.put("out_refund_no", String.valueOf(payorderid));
		} else {
			paramMap.put("refund_id", payOrder.getOuttradeno());
		}
		try {
			paramMap.put("sign", config.getSignStr(paramMap));
		} catch (SDKRuntimeException e) {
			logger.error("生成微信退款状态查询签名失败"+e.getMessage());
		}
		String xmlInfo = XMLParser.getXmlStrFromMap(paramMap);
		logger.info("微信退款查询请求参数："+xmlInfo);
		Map<String, String> refunded = new HashMap<String, String>();
		try {
			int status = PayOrder.STATUS_FAIL;
			HttpsRequest request = new HttpsRequest(config.getApiWeixinCertLocalPath(), config.getApiWeixinCertPassword());

			String result = request.post(config.getWeixinRefundQueryUrl(), xmlInfo, config.getApiWeixinCertLocalPath(), config.getApiWeixinCertPassword());
			logger.info("微信退款响应结果："+result);
			Map<String,String> resultMap = null;
			if(null != result && !"".equals(result)){
				resultMap = XMLParser.parseXml(result);
			}
			refunded.put("code", "fail");
			if("SUCCESS".equals(resultMap.get("result_code"))){
				String refundStatus = resultMap.get("refund_status_0");
				if(refundStatus.equals("SUCCESS")) {//退款成功
					status = PayOrder.STATUS_SUCCESS;
				} else if(refundStatus.equals("PROCESSING")){//退款进行中
					status = PayOrder.STATUS_NO;
				} else {//其他
					status = PayOrder.STATUS_FAIL;
				}
				refunded.put("code", "success");
			} else {
				refunded.put("message", resultMap.get("err_code_des"));
			}
//			this.notifyService.weixinRefundQuery(payOrder,status);
//			this.updatePayOrder(payorderid,status,null,memo, true);
		} catch (Exception e) {
			logger.info("微信退款查询请求异常：",e);
			refunded.put("message", e.getMessage());
		}
		return refunded;
	}
	
	/**
	 * 对账单
	 * @return
	 */
	@Override
	public Map<String, PayOrder> reconciliation(PayParams params) {
		String billDateStr = DateUtil.getDate(params.getBillDate());
		File file = this.getBillFile(billDateStr);
		try {
			return WeixinBillParser.parser(file);
		} catch (IOException e) {
			logger.info("微信解析异常：",e);
			return null;
		}
	}
	/**
	 * 获得账单文件
	 * @param billDate
	 * @return
	 */
	private File getBillFile(String billDate) {
		PaymentConfig config = PaymentConfig.getInstance();
		Map<String, String> map = this.getWeiXinCommonParam(config);
		map.put("bill_date", billDate);
		map.put("bill_type", "ALL");
		try {
			map.put("sign", config.getSignStr(map));
		} catch (SDKRuntimeException e) {
			logger.error("生成微信预订单签名失败"+e.getMessage());
		}
		String xmlInfo = XMLParser.getXmlStrFromMap(map);
		String filePath = config.getBillPath() + "weixin_" + billDate + ".txt";
		return HttpRequest.downFile(config.getWeixinDownLoadBillUrl(), 
				filePath, xmlInfo);
		
	}
	
	
	private Map<String,String> getWeiXinCommonParam(PaymentConfig config) {
		Map<String, String> map = new HashMap<String, String>();
		map.put("appid", config.getApiWeixinAppId());
		map.put("mch_id", config.getApiWeixinMchId());//商户号
		map.put("nonce_str", config.getApiWeixinNonceStr());
		return map;
	}
	
	
}
